package in.muton.edushare.panels.upload;

import in.muton.edushare.EduShareSession;
import in.muton.edushare.panels.upload.exceptions.FileProcessingException;
import in.muton.edushare.panels.upload.exceptions.InvalidUploadFileException;
import in.muton.edushare.panels.upload.postProcessors.PostProcessingFactory;
import in.muton.wicket.extensions.upload.UploadProcessInfo;
import in.muton.wicket.extensions.upload.UploadProcessWebRequest;

import java.io.File;
import java.io.IOException;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.protocol.http.WebRequest;

import com.edushare.common.dao.FileUploadInfoDAO;
import com.edushare.common.util.StringUtil;
import com.edushare.common.util.URLUtils;
import com.edushare.common.util.UploadUtils;
import com.edushare.common.vo.FileDirectories;
import com.edushare.common.vo.FileUploadInfo;
import com.edushare.common.vo.FilesUploaded;
import com.edushare.common.vo.Users;
import com.edushare.common.vo.interfaces.IProcessor;

public class Uploader {

	private static Log log = LogFactory.getLog(Uploader.class);
	private FileUpload fileUpload;
	private FileDirectories destination;
	private FileUploadInfo fileUploadInfo;
	private boolean fileProcessingSucessful = true;

	public Uploader(FileUpload fileUpload, FileDirectories destinationDirectory){
		this.fileUpload = fileUpload;
		this.destination = destinationDirectory;
	}
	
	public FilesUploaded startUploading() throws InvalidUploadFileException, IOException, FileProcessingException{
		
		if(!isvalidFile())
			throw new InvalidUploadFileException("Invalid file type.");
		
		//register that file upload to database
		registerFileUploadToDb();
		return saveFileToDisk();
		
	}

	
	/**
	 * Primary validation of file based on fileName.
	 * @return
	 */
	private boolean isvalidFile() {
		boolean isValid = false;
		String origFileName = "";
		
		if(fileUpload==null) //no file
			return false;
		
		if(fileUpload.getClientFileName().indexOf('.')<1) //no file name
			return false;
		
		origFileName = fileUpload.getClientFileName();		
		String extn = origFileName.substring(origFileName.indexOf('.')+1);		
		String allowedExt = "";
		
		if(
				destination.getDirType()!=null && 
				StringUtil.isValid(destination.getDirType().getValidFileExtn())
		){
			allowedExt = destination.getDirType().getValidFileExtn();
		}
		
		if(!StringUtil.isValid(extn)){//no extn, dont allow
			isValid = false;
		}else if(!StringUtil.isValid(allowedExt)){//nothing specified, allow all
			isValid = true;
		}else{
			if(allowedExt.contains(","+extn.toLowerCase()+",")){
					isValid = true;
			}
		}
		
		return isValid;
	}
	
	/**
	 * 
	 * @return fileUploadInfo that contains information about process completion, location in disc etc
	 */
	private void registerFileUploadToDb() {
		FileUploadInfo fuvo = new FileUploadInfo();
		fuvo.setFileOrigName(fileUpload.getClientFileName());
		fuvo.setUploadStatus(FileUploadInfo.UPLOAD_STATUS_PREUPLOAD);
		fuvo.setUploadTimestamp(new Date(System.currentTimeMillis()));
		new FileUploadInfoDAO(EduShareSession.get().getSessionORMInterface()).createFileUploadInfo(fuvo);
		this.fileUploadInfo =  fuvo;
	}
	
	
	/**
	 * Writes to Disk, Updates upload progress in DB and for progress bar, call processor method, calls insert to DB as EduShare file object
	 * @return 
	 * @throws IOException
	 * @throws FileProcessingException
	 */
	private FilesUploaded saveFileToDisk() throws IOException, FileProcessingException{
		
		String finalFileName = getFileName(fileUpload, fileUploadInfo);
		String uploadDirectory = UploadUtils.getMediaUploadPath();
		File storageFile = new File(uploadDirectory,finalFileName);
		
		//write to disk
		storageFile.createNewFile();
		fileUpload.writeTo(storageFile);
		
		// update fileInfo
		fileUploadInfo.setUploadStatus(FileUploadInfo.UPLOAD_STATUS_PREPROCESS);
		fileUploadInfo.setAbsoluteFilePath(uploadDirectory+"/"+finalFileName);
		fileUploadInfo.setFilePath(UploadUtils.getRelativeUploadPath(uploadDirectory+"/"+finalFileName));
		new FileUploadInfoDAO(EduShareSession.get().getSessionORMInterface()).saveFileUploadInfo(fileUploadInfo);
		
		//call processor
		if(destination.getFileProcessStrategy()!=null){
			invokeFileProcessor();
		}

		// update status
		HttpServletRequest req = ((WebRequest)RequestCycle.get().getRequest()).getHttpServletRequest();
		UploadProcessInfo info = (UploadProcessInfo)UploadProcessWebRequest.getUploadInfo(req);
		
		FilesUploaded finalFileUpload = null;
		if(fileProcessingSucessful){
			info.setProcessingStatus(UploadProcessInfo.PROCESSING_ENDED);
			fileUploadInfo.setUploadStatus(FileUploadInfo.UPLOAD_STATUS_POSTPROCESS);
			finalFileUpload = updateStorageDirectory();
		}else{
			info.setProcessingStatus(UploadProcessInfo.PROCESSING_FAILED);
			fileUploadInfo.setUploadStatus(FileUploadInfo.UPLOAD_STATUS_PROCESSING_FAILED);
		}
		new FileUploadInfoDAO(EduShareSession.get().getSessionORMInterface()).saveFileUploadInfo(fileUploadInfo);
		
		return finalFileUpload;
	}
	
	/** gets a safe file name by modifying the original one */
	private String getFileName(FileUpload fileUpload, FileUploadInfo fuvo){
		String origFileName = fileUpload.getClientFileName();
		String proposedFileName = origFileName.substring(0, origFileName.lastIndexOf('.'));//use originally uploaded fileName
		String fileExt = origFileName.substring(origFileName.lastIndexOf('.'));
		String finalFileName = URLUtils.escapeSplChars(proposedFileName+"_"+ fuvo.getFileUploadId())+fileExt;
		return finalFileName;
	}
	
	/**
	 * Calls appropriate file processor
	 * @throws FileProcessingException
	 */
	private void invokeFileProcessor() throws FileProcessingException{
		log.debug("Inside processor");
		IProcessor processor = PostProcessingFactory.getProcessor(destination.getFileProcessStrategy(), fileUploadInfo);
		try{
			processor.process();
		}catch (Exception e) {
			fileProcessingSucessful = false;
			fileUploadInfo.setUploadStatus(FileUploadInfo.UPLOAD_STATUS_PROCESSING_FAILED);
			new FileUploadInfoDAO(EduShareSession.get().getSessionORMInterface()).saveFileUploadInfo(fileUploadInfo);
			log.error("ERROR DURING UPLOAD POST-PROCESSING. ", e);
			throw new FileProcessingException("Exception while processing the uploaded file. " +
					"File Info:["+
					"orig Name: "+ fileUploadInfo.getFileOrigName()+", "+
					"file Path: "+ fileUploadInfo.getFilePath()+", "+ 
					"upload Id: "+ fileUploadInfo.getFileUploadId()+"]");
		}
	}
	
	/**
	 * Updates the containing directory and registers as a FilesUploaded object
	 * @return 
	 */
	private FilesUploaded updateStorageDirectory(){
		Users uploader  = EduShareSession.get().getLoggedInUser();
		FilesUploaded fuploaded = new FilesUploaded();
		fuploaded.setFileOrigName(fileUploadInfo.getFileOrigName());
		fuploaded.setFileRelativePath(fileUploadInfo.getFilePath());
//		fuploaded.setPermission(destination.getPermissionType());
		fuploaded.setUploader(uploader);
		fuploaded.setUploadTimestamp(new Date());
		fuploaded.setFileDirectory(destination);
		destination.setUpdateTimestamp(new Date());
		if(destination.getFileProcessStrategy()!=null)
			fuploaded.setProcessingStrategy(destination.getFileProcessStrategy().getProcessStrategyType());
		return fuploaded;
	}

}
